﻿/*  
 * Helix Engine
 * http://helixengine.codeplex.com/
 * 
 * Copyright (c) 2010 - 2011 Joe Hegarty
 *  
 * This source code is subject to the terms of the Microsoft Public License (Ms-PL). 
 *  
 * Redistribution and use in source and binary forms, with or without modification, 
 * is permitted provided that redistributions of the source code retain the above 
 * copyright notices and this file header. 
 * 
 * For details please see http://www.opensource.org/licenses/ms-pl.html 
 *  
 * All other rights reserved. 
 *
 */  


using System;
using System.Windows.Controls;
using System.Windows.Media;

using HelixEngine;
using HelixEngine.Maths;
using HelixEngine.Exceptions;

using HelixGame.World;
using HelixGame.Operations;

using HelixEngine.Resources;

namespace HelixGame
{
    /// <remarks>
    /// Singleton representing access to the Helix Game
    /// </remarks>
    /// <permission cref="System.Security.PermissionSet">public sealed</permission>
    public class Game
    {
        #region Singleton

        static Game _gameInstance = null;

        /// <summary>
        /// Initialise the Helix Game
        /// </summary>
        /// <exception cref="AlreadyInitialisedException">
        /// Thrown when an instance of the game has already been initialised
        /// </exception>
        public static void InitialiseGame(Image sceneRoot)
        {
            if (_gameInstance == null)
            {
                _gameInstance = new Game(sceneRoot);
            }
            else
            {
                throw new AlreadyInitialisedException("_gameInstance");
            }
        }

        /// <summary>
        /// Returns the active instance of the game
        /// </summary>
        /// <returns>An instance of HelixGame</returns>
        /// <exception cref="NotInitialisedException">
        /// Thrown when an instance of the game does not exist
        /// </exception>
        public static Game Get()
        {
            if (_gameInstance != null)
            {
                return _gameInstance;
            }
            else
            {
                throw new AlreadyInitialisedException("_gameInstance");
            }
        }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        public WorldManager WorldManager
        {
            get { return _worldManager; }
        }

        WorldManager _worldManager;
        OperationsManager _operationsManager;

        EngineTime _engineTime = null;
        int _startTime;

        /// <summary>
        /// The constructor for HelixGame. Creates an instance of the game
        /// </summary>
        /// <param name="sceneRoot">The root of the scene to render into</param>
        public Game(Image sceneRoot)
        {
            // Initialise the engine
            Engine.InitialiseEngine(sceneRoot);

            _worldManager = new WorldManager();
            _operationsManager = new OperationsManager();

            _startTime = Environment.TickCount;
            _engineTime = new EngineTime(TimeSpan.Zero, TimeSpan.Zero, TimeSpan.Zero, TimeSpan.Zero);
            CompositionTarget.Rendering += new EventHandler(Tick);
            
        }

        void Tick(object sender, EventArgs e)
        {
            int timeSinceAppStart = Environment.TickCount - _startTime;
            TimeSpan updateTime = TimeSpan.FromMilliseconds(timeSinceAppStart - _engineTime.TotalRealTime.TotalMilliseconds);
            _engineTime.ElapsedEngineTime = updateTime;
            _engineTime.TotalEngineTime = _engineTime.TotalEngineTime.Add(updateTime);
            _engineTime.ElapsedRealTime = updateTime;
            _engineTime.TotalRealTime = _engineTime.TotalRealTime.Add(updateTime);

            Update(_engineTime);
        }

        void Update(EngineTime time)
        {

            // Game updates
            _operationsManager.Update(time);
            _worldManager.Update(time);
            
            // Engine update
            Engine.Get().Update(time);
        }
    }
}
